package org.jsets.fastboot.persistent.interceptor;

import java.util.Objects;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.jsets.fastboot.util.SpringContextHolder;
import org.jsets.fastboot.persistent.config.PersistentContext;
import org.jsets.fastboot.persistent.config.PersistentAnnotationResolver;
import org.jsets.fastboot.persistent.enums.SqlAction;
import org.jsets.fastboot.persistent.metadata.FieldEncryptOption;
import org.jsets.fastboot.persistent.metadata.FieldFillOption;
import org.jsets.fastboot.persistent.metadata.PersistentOption;
import org.jsets.fastboot.persistent.IEncryptService;
import org.jsets.fastboot.persistent.IFieldFillService;
import org.springframework.beans.factory.annotation.Autowired;
import cn.hutool.core.util.ReflectUtil;

/**
 * 
 * Executor 拦截器
 * 
 * @author wangjie
 *
 */
@Intercepts({ 
	@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
	@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class }),
	@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class })
})
public class ExecutorInterceptor implements Interceptor {

	@Autowired
	private IEncryptService encryptService;
	
	private IFieldFillService fieldFillService;
	private boolean timeConsuming;

	public ExecutorInterceptor(boolean timeConsuming) {
		this.timeConsuming = timeConsuming;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
		Object object = invocation.getArgs()[1];
		if (InterceptorUtils.isUpdataInvocation(invocation)) {
			Object entity = InterceptorUtils.getEntity(object);
			if(Objects.nonNull(entity)) {
				
				PersistentOption option = PersistentAnnotationResolver.getOption(entity.getClass());
				if (Objects.nonNull(option)) {
					// 自动填充
					if (option.isFillEnable()) {
						for (FieldFillOption fo : option.getFieldFills()) {
							Object value = null;
							Object rawValue = ReflectUtil.getFieldValue(entity, fo.getField());
							if (InterceptorUtils.isInsertSqlCommand(statement)&& (SqlAction.INSERT.equals(fo.getSqlAction())||SqlAction.INSERT_UPDATE.equals(fo.getSqlAction()))) {
								value = this.getFieldFillService().getFillValueOnInsert(fo.getField(), rawValue);
							}
							if (InterceptorUtils.isUpdateSqlCommand(statement)&& (SqlAction.UPDATE.equals(fo.getSqlAction())||SqlAction.INSERT_UPDATE.equals(fo.getSqlAction()))) {
								value = this.getFieldFillService().getFillValueOnUpdate(fo.getField(), rawValue);
							}
							if (null != value) {
								ReflectUtil.setFieldValue(entity, fo.getField(), value);
							}
						}
					}

					// 加密
					if (option.isEncryptEnable()) {
						for (FieldEncryptOption eo : option.getFieldEncrypts()) {
							if (eo.isLazy() && !PersistentContext.isEncryptNecessary()) {
								continue;
							}
							if (!eo.isEncryptOnStore()) {
								continue;
							}
							Object value = ReflectUtil.getFieldValue(entity, eo.getField());
							if (null == value) {
								continue;
							}
							String plaintext = (String) value;
							String ciphertext = this.getEncryptService().encrypt(eo.getAlgorithm(), plaintext);
							ReflectUtil.setFieldValue(entity, eo.getField(), ciphertext);
						}
					}
				}
				
			}
		}

		if(this.timeConsuming) {
			long start = System.currentTimeMillis();
			Object result = invocation.proceed();
			long end = System.currentTimeMillis();
		    long cost = end - start;
			System.out.println("==> ["+statement.getId()+"]耗时: "+cost+"(毫秒)");
			return result;
		}
		
		return invocation.proceed();
	}

	private IFieldFillService getFieldFillService() {
		if (null != this.fieldFillService) {
			return this.fieldFillService;
		}

		synchronized (this) {
			if (null != this.fieldFillService) {
				return this.fieldFillService;
			}
			this.fieldFillService = SpringContextHolder.getBean(IFieldFillService.class);
			return this.fieldFillService;
		}
	}

	private IEncryptService getEncryptService() {
		return this.encryptService;
	}
}